package com.example.hello.views.tracks;

import com.example.hello.*;
import com.example.hello.POJO.*;
import com.example.hello.views.Collection;
import com.example.hello.views.assignments.Assignment;
import com.example.hello.views.assignments.Assignments;
import com.example.hello.views.organizations.Organizations;
import com.vaadin.navigator.ViewChangeListener;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.VerticalLayout;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class Track extends Collection {

    private static UserPOJO currentUser;
    private List<AssignmentPOJO> assignments;

    private VerticalLayout addLayout = new VerticalLayout();
    private HorizontalLayout viewLayout = new HorizontalLayout();
    private BootstrapButton back = new BootstrapButton("Back");

    private HorizontalLayout add = new HorizontalLayout();
    private VerticalLayout description = new VerticalLayout();

    private StringWrap selectedAssignment = new StringWrap();

    private TrackPOJO track;

    @Override
    public void enter(ViewChangeListener.ViewChangeEvent event) {
        currentUser = ((HelloUI) HelloUI.getCurrent()).getUser();
        if (currentUser == null) currentUser = new UserPOJO();
//        assignments = Assignments.GETAssignments("?track=" + track.getEncodedName());
//        if (assignments == null) assignments = new LinkedList<>();
//        track = Track.GETTrack(track.getEncodedName());
//        if (this.track == null) this.track = new TrackPOJO();

        addLayout.setVisible(currentUser.getOrgRole(track.getOrganization()).ordinal() >= 2);
        description.setVisible(currentUser.getOrgRole(track.getOrganization()).ordinal() >= 2);
    }

    public Track(String NAME, TrackPOJO track) {
        super(NAME);
        this.track = track;

        currentUser = ((HelloUI) HelloUI.getCurrent()).getUser();
        if (currentUser == null) currentUser = new UserPOJO();
        assignments = Assignments.GETAssignments("?track=" + track.getEncodedName());
        if (assignments == null) assignments = new LinkedList<>();
        if (this.track == null) this.track = new TrackPOJO();

        initAdd();
        initDisplay();

        addComponent(addLayout);
        addComponent(viewLayout);
        addComponent(back);
        setComponentAlignment(viewLayout, Alignment.TOP_CENTER);
        setExpandRatio(viewLayout, 1);
    }
    
    public void initAdd() {
        add.setWidth("800px");
        
        final Heading nameHeading = new Heading("Name", 6);
        final TextField nameField = new TextField();
        final Heading descriptionHeading = new Heading("Description", 6);
        final TextArea descriptionField = new TextArea();
        descriptionField.setWidth("220px");
        descriptionField.setHeight("228px");
        final Heading submissionPointsHeading = new Heading("Submission Points", 6);
        final TextField submissionPointsField = new TextField();
        submissionPointsField.setWidth("50px");
        final Heading reviewPointsHeading = new Heading("Review Points", 6);
        final TextField reviewPointsField = new TextField();
        reviewPointsField.setWidth("50px");
        final BootstrapButton assignmentSubmit = new BootstrapButton("Add Assignment");
        assignmentSubmit.setWidth("120px");

        assignmentSubmit.addListener(new Listener() {
            @Override
            public void componentEvent(Event event) {
                AssignmentPOJO assignment = new AssignmentPOJO(
                        nameField.getEncodedValue(),
                        descriptionField.getValue(),
                        track.getEncodedName(),
                        new ArrayList<String>(),
                        Integer.valueOf(submissionPointsField.getValue()),
                        Integer.valueOf(reviewPointsField.getValue())
                );
                addAssignment(track, assignment, true);
                refreshView(track);
            }
        });
        TextField.enterKeyHandler(assignmentSubmit, nameField);
        TextField.enterKeyHandler(assignmentSubmit, descriptionField);
        TextField.enterKeyHandler(assignmentSubmit, submissionPointsField);
        TextField.enterKeyHandler(assignmentSubmit, reviewPointsField);

        back.addListener(new Listener() {
            @Override
            public void componentEvent(Event event) {
                getUI().getNavigator().navigateTo(Tracks.NAME);
            }
        });

        add.addComponents(
                nameHeading, new HSpacer(5), nameField,
                new HSpacer(20),
                submissionPointsHeading, new HSpacer(5), submissionPointsField,
                new HSpacer(20),
                reviewPointsHeading, new HSpacer(5), reviewPointsField,
                new HSpacer(20),
                assignmentSubmit
        );

        add.setComponentAlignment(nameHeading, Alignment.MIDDLE_LEFT);
        add.setComponentAlignment(nameField, Alignment.MIDDLE_LEFT);
        add.setComponentAlignment(submissionPointsHeading, Alignment.MIDDLE_LEFT);
        add.setComponentAlignment(submissionPointsField, Alignment.MIDDLE_LEFT);
        add.setComponentAlignment(reviewPointsHeading, Alignment.MIDDLE_LEFT);
        add.setComponentAlignment(reviewPointsField, Alignment.MIDDLE_LEFT);
        add.setExpandRatio(nameField, 1);

        addLayout.addComponents(new VSpacer(10), add, new VSpacer(20));
        addLayout.setComponentAlignment(add, Alignment.MIDDLE_CENTER);

//        description.addComponent(new HSpacer(40));
        description.addComponents(descriptionHeading, descriptionField);
        description.setWidth("220px");
        description.setHeight("256px");
        description.setComponentAlignment(descriptionHeading, Alignment.MIDDLE_LEFT);
        description.setComponentAlignment(descriptionField, Alignment.MIDDLE_LEFT);
        description.setExpandRatio(descriptionField, 1);
    }
    
    public void initDisplay() {
        viewLayout.addComponents(
                displayListBox(assignments, Assignments.NAME, selectedAssignment, false, true),
                new HSpacer(40),
                description
        );
    }

    /**
     * These static methods are intended to be used as an alternative to the HttpRequest class
     * directly. The GET(String get) method can technically return any value type, however this
     * is useful if the user of this methods knows the type of the value that should be returned.
     *
     * These methods currently reside in this View due to this View's name relationship with
     * these methods function. It might be a good idea to move these methods elsewhere later
     * (perhaps in a helper class whose sole purpose is to return the proper POJO).
     */
    public static TrackPOJO GET(String get) {
        TrackPOJO track = new TrackPOJO();
        try {
            String json = HttpRequest.GET(get);
            ObjectMapper mapper = new ObjectMapper();
            track = mapper.readValue(json, new TypeReference<TrackPOJO>() {
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        return track;
    }

    public static TrackPOJO GETTrack(String trackName) {
        TrackPOJO track = new TrackPOJO();
        try {
            String json = HttpRequest.GET(Tracks.NAME + "/" + trackName);
            ObjectMapper mapper = new ObjectMapper();
            track = mapper.readValue(json, new TypeReference<TrackPOJO>() {
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        return track;
    }

    /**
     * This method is intended to be called after changes have been made to the resource
     * associated with this View. Since the View is persistent due to its initialization
     * (unlike Users, Organizations, etc.), a new View must be created to reflect any changes.
     */
    private static void refreshView(TrackPOJO track) {
        HelloUI.getCurrent().getNavigator().removeView(Tracks.NAME + "/" + track.getEncodedName());
        Track newView = new Track(track.getEncodedName(), track);
        HelloUI.getCurrent().getNavigator().addView(newView.NAME, newView);
        HelloUI.getCurrent().getNavigator().navigateTo(newView.NAME);
    }

    /**
     * The methods below are intended to provide the proper JOIN mechanism between
     * related resources.
     */
    public static boolean changeName(TrackPOJO track, String oldName, String newName) {
        Set<OrganizationPOJO> orgs = Organizations.GETOrganizations("?track=" + track.getEncodedName());
        List<AssignmentPOJO> assignments = Assignments.GETAssignments("?track=" + track.getEncodedName());

        track.setName(newName);
        for (OrganizationPOJO org : orgs) {
            org.removeTrack(oldName);
            org.addTrack(newName);
            org.update();
        }
        for (AssignmentPOJO assignment : assignments) {
            assignment.removeReviewer(oldName);
            assignment.addReviewer(newName);
            assignment.update();
        }

        return POJO.update(Tracks.NAME + "/" + oldName, track);
    }
    
    public static boolean changeOrganization(TrackPOJO track, OrganizationPOJO fromOrg, OrganizationPOJO toOrg) {
        track.setOrganization(toOrg.getEncodedName());
        fromOrg.removeTrack(track.getEncodedName());
        toOrg.addTrack(track.getEncodedName());
        return track.update() && fromOrg.update() && toOrg.update();
    }

    public static boolean addAssignment(TrackPOJO track, AssignmentPOJO assignment, boolean create) {
        track.addAssignment(assignment.getEncodedName());
        assignment.setTrack(track.getEncodedName());
        return track.update() && (create ? assignment.create() : assignment.update());
    }

    public static boolean removeAssignment(TrackPOJO track, AssignmentPOJO assignment) {
        track.removeAssignment(assignment.getEncodedName());
        assignment.setTrack(AssignmentPOJO.UNASSIGNED);
        return track.update() && assignment.update();
    }

    public static boolean addBadge(TrackPOJO track, BadgePOJO badge, boolean create) {
        track.addBadge(badge.getEncodedName());
        return track.update() && (!create || badge.create());
    }

    public static boolean removeBadge(TrackPOJO track, BadgePOJO badge) {
        track.removeBadge(badge.getEncodedName());
        return track.update();
    }

}